#! /usr/bin/python
import glob, sys, os
import DebugMsgDef

# DEFAULT_TIME_GAP = 1000*600 # 10 min.

DEFAULT_TIME_GAP = 1000 # 10 sec.
##############################################
# Read csv files.                            #
# Return: node list, a dict of message list. #
##############################################
def readFile(fn):
  nodeID = int(fn[fn.find('node') + 4:-4])
  msgList = []
  print 'Reading', fn
  dfile = open(fn)
  aline = dfile.readline()
  if 'NET_C_' not in aline:
    print 'Found uncovered csv files. converting...'
    dfile.close()
    import dec2hex
    dec2hex.type2String(fn)
    dfile = open(fn)
  else:
    dfile.seek(0)
  for aline in open(fn):
    msg = SerialMessage(aline)
    # if (getType(msg) in DebugMsgDef.TxMessageTypes) or \
    #   (getType(msg) in DebugMsgDef.PdMessageTypes) or \
    #   (getType(msg) in DebugMsgDef.TreeMessageTypes):
    #  msgList.append(msg)
    msgList.append(msg)
  print len(msgList), 'message found.'
  return nodeID, msgList

def categorizeMessage(msgList):
  tp = DebugMsgDef.debugMsgTypes.values()
  ep = [[] for i in range(len(DebugMsgDef.debugMsgTypes))]
  msgCata = dict(zip(tp,ep))
  for i in xrange(len(msgList)):
    msgCata[getType(msgList[i])].append(i)
  return msgCata

"""
Generate a csv file, list useful types of debug messages
and their occurances.
"""
def writeSummary(msgType, fn):
  types = DebugMsgDef.TxMessageTypes
  types.extend(DebugMsgDef.TreeMessageTypes)
  types.extend(DebugMsgDef.PdMessageTypes)
  sumStr = 'NodeID,' + ','.join(types) + '\n'
  nodes = msgType.keys()
  nodes.sort()
  for nodeID in nodes:
    msgDict = msgType[nodeID]
    sumStr += str(nodeID) + ','
    sumStr += ','.join([str(len(msgDict[tp])) for tp in types]) + '\n'
  sumFile = open(fn, 'w')
  sumFile.write(sumStr)
  sumFile.close()

def findTxMessage(msgList,typeDict,seq,origin,types,startTime,endTime):
  foundMsg = []
  # find the first msg if seq = -1
  if seq == -1:
    for idx in xrange(len(msgList)):
      msg = msgList[idx]
      if (getOrigin(msg) == origin) and (getType(msg) in types):
        seq = getSeq(msg) # append will happen in next block
        startTime = getTime(msg)
        endTime = startTime + DEFAULT_TIME_GAP
        break

  if seq != -1:
    for tp in types:
      for idx in typeDict[tp]:
        msg = msgList[idx]
        if getTime(msg) < startTime:
          continue
        elif getTime(msg) > endTime:
          break
        else:
          if (getOrigin(msg) == origin) and (getSeq(msg) == seq):
            foundMsg.append(idx)
            endTime = getTime(msg) + DEFAULT_TIME_GAP
  foundMsg.sort()
  return foundMsg
  
def findMsgByTypes(msgList,typeDict,seq,origin,time,findTypes):
  if seq != -1:
    startTime = time - int(DEFAULT_TIME_GAP / 2)
    endTime = time + DEFAULT_TIME_GAP
  else:
    startTime = 0
    endTime = 0
  return findTxMessage(msgList,typeDict,seq,origin,findTypes,startTime,endTime)


def findTxSendMessage(msgList,typeDict,seq,origin,time):
  if seq != -1:
    startTime = time - int(DEFAULT_TIME_GAP / 2)
    endTime = time + DEFAULT_TIME_GAP
  else:
    startTime = 0
    endTime = 0
  return findTxMessage(msgList,typeDict,seq,origin,DebugMsgDef.TxSendMessages,startTime,endTime)

def findTxRcvMessage(msgList,typeDict,seq,origin,time):
  if seq != -1:
    startTime = time - int(DEFAULT_TIME_GAP / 2)
    endTime = time + DEFAULT_TIME_GAP
  else:
    startTime = 0
    endTime = 0
  return findTxMessage(msgList,typeDict,seq,origin,DebugMsgDef.TxRcvMessages,startTime,endTime)

"""
find every packet's path for every nodes
Find logic:
1) Get sent message of a packet. (UID: seq, orig, time)
2) Find all matching TX+RCV+SNOOP messages with the UID.
   Store in 3 dicts (node id -> list of found msg idx)
3) Sort the node ID according to the send seq.
4) return the path list.
"""
def packetPath(msgDict, msgTypeDict, rootID):
  pktDict = {}
  for nodeID in msgDict.keys():
    if nodeID == rootID:
      continue
    print 'Tracing packets in node', nodeID

    pktList = []
    
    # 1) Get sent message of a packet. (UID: seq, orig, time)
    foundList = findMsgByTypes(msgDict[nodeID], msgTypeDict[nodeID], -1, nodeID, 0, DebugMsgDef.TxSendMessages)
    while len(foundList) > 0:
      lastFound = msgDict[nodeID][foundList[-1]]
      seq = getSeq(lastFound)
      origin = nodeID
      time = getTime(lastFound)

      txList = []
      hopList = []
      
      # 2) Find all matching TX+RCV+SNOOP messages with the UID.
      rcvDict = {}
      txDict = {}
      snpDict = {}
      for hopID in msgDict.keys():
        rcvFound = findTxRcvMessage(msgDict[hopID], msgTypeDict[hopID], seq, origin, time, DebugMsgDef.TxRcvMessages)
        rcvDict[hopID] = rcvFound;
        txFound = findTxSendMessage(msgDict[hopID], msgTypeDict[hopID], seq, origin, time, DebugMsgDef.TxSendMessages)
        txDict[hopID] = txFound;
        snpFound = findTxSendMessage(msgDict[hopID], msgTypeDict[hopID], seq, origin, time, DebugMsgDef.TxSnoopMessages)
        snpDict[hopID] = snpFound;
      
      # 3) Sort the 3 dicts according to the send path.
      # new packet ID: sender, seq, origin, time
      # start from the sender, check # of TX msg
      #   Then check # of FE_RCV/FE_SNOOP of all other nodes
      #     If RCVed by more than 2 nodes, loop
      #   Set RCV node as the new sender if it's not root
      pktPath = [origin];
      currHop = origin
      while currHop != rootID
        for rcvID in rcvDict.keys():
			for rcvIdx in rcvDick[rcvID]:
				if getSrc(msgDict[rcvID][rcvIdx]) == currHop:
					
      
      
      foundList = findTxSendMessage(msgDict[nodeID], msgTypeDict[nodeID], seq, origin, time)
      
    pktDict[nodeID]=pktList
    print 'Done.', len(pktList), 'packet traced.'
  return pktDict
    
    
    

    foundList = findTxSendMessage(msgDict[nodeID], msgTypeDict[nodeID], seq, origin, time)
    while len(foundList) > 0:
      # TODO: check for duplicate send/fwd.
      lastFound = msgDict[nodeID][foundList[-1]]
      seq = getSeq(lastFound)
      time = getTime(lastFound)
      txList = []
      hopList = []
      pathStatus = 'LOST'
      txList.append(len(foundList))
      hopList.append(nodeID)

      # simple (and burtual) approach: find all related msg in all node.
      for hopID in msgDict.keys():
        if hopID == nodeID:
          continue
        rcvFound = findTxRcvMessage(msgDict[hopID], msgTypeDict[hopID], seq, origin, time)
        txFound = findTxSendMessage(msgDict[hopID], msgTypeDict[hopID], seq, origin, time)
        if len(rcvFound) == 0 and len(txFound) > 0:
          print 'Send without Rcv @ node',hopID, 'idx', txFound[-1]
        elif len(rcvFound) > 0 and len(txFound) == 0 and hopID != rootID:
          print 'PKT lost @ node', hopID, 'idx', rcvFound[-1]

        if len(rcvFound) > 0 or len(txFound) > 0:
          txList.append(len(txFound))
          hopList.append(hopID)
          if hopID == rootID:
            pathStatus = 'DELIVERED'

      # Finish tracing one packet. record: pkt tuple, tx list, hop list
      # record packet list. value: tuple of (pkt tuple, txList, hopList)
      pktList.append( ((seq,origin,time), hopList, txList, pathStatus) )
      # now looking for the next packet.
      seq = (seq + 1) % 255
      # hack for not countious packets. Just look for the next one.
      lastidx = foundList[-1] + 1
      for idx in xrange(lastidx, len(msgDict[nodeID])):
        msg = msgDict[nodeID][idx]
        if (getOrigin(msg) == origin) and (getType(msg) in DebugMsgDef.TxSendMessages):
          seq = getSeq(msg) # append will happen in next block
          time = getTime(msg)
          break

      foundList = findTxSendMessage(msgDict[nodeID], msgTypeDict[nodeID], seq, origin, time)

    # all Tx packets in currHop are taken care of. record everything.
    pktDict[nodeID]=pktList
    print 'Done.', len(pktList), 'packet traced.'
  return pktDict

def writePath(pathDict, surfix):
  # clean the directory
  for fn in glob.glob(surfix + '*'):
    os.remove(fn)

  # summary: node,# pkt, avgr. PL, delivered, lost, loop
  sumDict={}
  sumpkt = 0; sumpl=0.0; sumtx=0;sumdlv=0; sumlost=0; sumloop=0
  for nodeID in pathDict.keys():
    pathList = pathDict[nodeID]
    sumDict[nodeID] = [nodeID,len(pathList)]
    pl=0.0; tx=0; dlv=0; lost=0; loop=0

    pathFile = open(surfix + str(nodeID), 'w')
    for (pktTuple, hopList, txList, status) in pathList:
      pl += len(hopList)
      tx += sum(txList)
      if status == 'DELIVERED':
        dlv += 1
      elif status == 'LOST':
        lost += 1
      elif status == 'LOOP':
        loop += 1
      else:
        print 'Invaild path status!'

      pathStr = ';'.join([ status, \
                          ','.join(str(i) for i in pktTuple), \
                          ','.join(str(i) for i in hopList),  \
                          ','.join(str(i) for i in txList), \
                          `len(hopList)`, `sum(txList)`, \
                          `max(txList)`, `min(txList)`])
      pathFile.write(pathStr+'\n')
    pathFile.close()

    sumpkt+=len(pathList);sumpl+=pl; sumtx+=tx; sumdlv+=dlv; sumlost+=lost; sumloop+=loop
    if (len(pathList) > 0):
      sumDict[nodeID].extend([pl / float(len(pathList)), tx, dlv,lost,loop])
    else:
      sumDict[nodeID].extend([ 0, tx, dlv,lost,loop])
  # also a summary file
  nodes = sumDict.keys()
  nodes.sort()
  if (sumpkt > 0):
    sumStr = ','.join(str(i) for i in [-1,sumpkt, sumpl/sumpkt, sumtx, sumdlv, sumlost, sumloop])
  else:
    sumStr = ','.join(str(i) for i in [-1,sumpkt, 0, sumtx, sumdlv, sumlost, sumloop])

  for nodeID in nodes:
    sumStr += '\n' + ','.join([str(i) for i in sumDict[nodeID]])
  pathFile = open(surfix + '_summary','w')
  pathFile.write(sumStr)
  pathFile.close()


"""
Main method
"""
def main():
  datadir = sys.argv[1]
  if datadir[-1] != '/':
    datadir = datadir + '/'

  csvlist = glob.glob(datadir + 'node*.csv')
  if len(csvlist) == 0:
    print "No csv files in the directory:", sys.argv[1]
    sys.exit(0)

  rootID = int(sys.argv[2])

  nodeList = [] # list of nodes
  msgDict = {} # key: node ID, value: list of msg
  msgTypeDict = {} # key: node ID. value: dict(msg type, msg idx)
  for csvfile in csvlist:
    nodeID, msgList = readFile(csvfile)
    if len(msgList) == 0:
      print 'Empyt file:', csvfile
      continue
    nodeList.append(nodeID)
    msgDict[nodeID]=msgList
    print 'Categorizing messages... ',
    msgTypeDict[nodeID] = categorizeMessage(msgList)
    print 'done'
  print len(nodeList), 'files read.'

  # start compute path. need:
  # 1) summary of debug msg: send, pd, and snoop(ones has better ETX)
  # 2) packet path, TX spend on each nodes, total TX, and TX of TP.
  # 3) save the summary and packet path to MATLAB readable format.

  writeSummary(msgTypeDict, datadir + 'summary')
  pathDict = packetPath(msgDict, msgTypeDict, rootID)
  writePath(pathDict, datadir + 'path')


######## ENTRTY #######
if(len(sys.argv) != 3):
  print 'parserMain <data dir> <root id>'
  sys.exit(0)

# determine what kind of messages based on dir name :P
if 'orig' in sys.argv[1]:
  from DebugMsgList_Original import *
  ORIGINAL = True
else:
  from DebugMsgList_Modified import *
  ORIGINAL = False

if __name__ == '__main__': main()

